/*-------------------------------------------------------------------------
 *
 * 前端代码的多字节字符打印支持
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/fe_utils/mbprint.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres_fe.h"

#include "fe_utils/mbprint.h"

#include "libpq-fe.h"


/*
 * 为了避免版本偏差问题，该文件不得使用来自pg_wchar.h的声明：
 * 我们处理的编码ID由我们链接的libpq.so决定，这可能与
 * 编译时看到的数字不匹配。 （如果该文件在libpq内部，
 * 问题就会消失......）
 *
 * 因此，我们有自己的pg_wchar定义，并实时获取
 * 任何需要的编码ID的值。
 */

typedef unsigned int pg_wchar;

static int fc_pg_get_utf8_id(void)
{
	static int	fc_utf8_id = -1;

	if (fc_utf8_id < 0)
		fc_utf8_id = pg_char_to_encoding("utf8");
	return fc_utf8_id;
}

#define PG_UTF8		fc_pg_get_utf8_id()


/*
 * 将一个UTF-8字符转换为Unicode码点。
 * 这是pg_utf2wchar_with_len的单字符版本。
 *
 * 此处没有错误检查，c必须指向足够长的字符串。
 */
static pg_wchar fc_utf8_to_unicode(const unsigned char *fc_c)
{
	if ((*fc_c & 0x80) == 0)
		return (pg_wchar) fc_c[0];
	else if ((*fc_c & 0xe0) == 0xc0)
		return (pg_wchar) (((fc_c[0] & 0x1f) << 6) |
						   (fc_c[1] & 0x3f));
	else if ((*fc_c & 0xf0) == 0xe0)
		return (pg_wchar) (((fc_c[0] & 0x0f) << 12) |
						   ((fc_c[1] & 0x3f) << 6) |
						   (fc_c[2] & 0x3f));
	else if ((*fc_c & 0xf8) == 0xf0)
		return (pg_wchar) (((fc_c[0] & 0x07) << 18) |
						   ((fc_c[1] & 0x3f) << 12) |
						   ((fc_c[2] & 0x3f) << 6) |
						   (fc_c[3] & 0x3f));
	else
		/* 这是一个故意无效的代码 */
		return 0xffffffff;
}


/*
 * Unicode 3.1符合性验证：对每个类别，它检查
 * 每个字节的组合，以确保它映射到有效范围内。它还
 * 返回-1对于以下UCS值：ucs > 0x10ffff，ucs & 0xfffe =
 * 0xfffe，0xfdd0 < ucs < 0xfdef，ucs & 0xdb00 = 0xd800（代理）
 */
static int fc_utf_charcheck(const unsigned char *fc_c)
{
	if ((*fc_c & 0x80) == 0)
		return 1;
	else if ((*fc_c & 0xe0) == 0xc0)
	{
		/* 两字节字符 */
		if (((fc_c[1] & 0xc0) == 0x80) && ((fc_c[0] & 0x1f) > 0x01))
			return 2;
		return -1;
	}
	else if ((*fc_c & 0xf0) == 0xe0)
	{
		/* 三字节字符 */
		if (((fc_c[1] & 0xc0) == 0x80) &&
			(((fc_c[0] & 0x0f) != 0x00) || ((fc_c[1] & 0x20) == 0x20)) &&
			((fc_c[2] & 0xc0) == 0x80))
		{
			int			fc_z = fc_c[0] & 0x0f;
			int			fc_yx = ((fc_c[1] & 0x3f) << 6) | (fc_c[0] & 0x3f);
			int			fc_lx = fc_yx & 0x7f;

			/* 检查0xfffe/0xffff，0xfdd0..0xfedf范围，代理 */
			if (((fc_z == 0x0f) &&
				 (((fc_yx & 0xffe) == 0xffe) ||
				  (((fc_yx & 0xf80) == 0xd80) && (fc_lx >= 0x30) && (fc_lx <= 0x4f)))) ||
				((fc_z == 0x0d) && ((fc_yx & 0xb00) == 0x800)))
				return -1;
			return 3;
		}
		return -1;
	}
	else if ((*fc_c & 0xf8) == 0xf0)
	{
		int			fc_u = ((fc_c[0] & 0x07) << 2) | ((fc_c[1] & 0x30) >> 4);

		/* 四字节字符 */
		if (((fc_c[1] & 0xc0) == 0x80) &&
			(fc_u > 0x00) && (fc_u <= 0x10) &&
			((fc_c[2] & 0xc0) == 0x80) && ((fc_c[3] & 0xc0) == 0x80))
		{
			/* 检查0xzzzzfffe/0xzzzzfffff */
			if (((fc_c[1] & 0x0f) == 0x0f) && ((fc_c[2] & 0x3f) == 0x3f) &&
				((fc_c[3] & 0x3e) == 0x3e))
				return -1;
			return 4;
		}
		return -1;
	}
	return -1;
}


static void fc_mb_utf_validate(unsigned char *fc_pwcs)
{
	unsigned char *fc_p = fc_pwcs;

	while (*fc_pwcs)
	{
		int			fc_len;

		if ((fc_len = fc_utf_charcheck(fc_pwcs)) > 0)
		{
			if (fc_p != fc_pwcs)
			{
				int			fc_i;

				for (fc_i = 0; fc_i < fc_len; fc_i++)
					*fc_p++ = *fc_pwcs++;
			}
			else
			{
				fc_pwcs += fc_len;
				fc_p += fc_len;
			}
		}
		else
			/* 我们跳过该字符 */
			fc_pwcs++;
	}
	if (fc_p != fc_pwcs)
		*fc_p = '\0';
}

/*
 * 公共函数：wcswidth和mbvalidate
 */

/*
 * pg_wcswidth是愚蠢的显示宽度函数。
 * 它假设所有内容都将在一行上出现。
 * OTOH，它比pg_wcssize更容易使用，如果这适合你的话。
 */
int pg_wcswidth(const char *fc_pwcs, size_t fc_len, int fc_encoding)
{
	int			fc_width = 0;

	while (fc_len > 0)
	{
		int			fc_chlen,
					fc_chwidth;

		fc_chlen = PQmblen(fc_pwcs, fc_encoding);
		if (fc_len < (size_t) fc_chlen)
			break;				/* 无效字符串 */

		fc_chwidth = PQdsplen(fc_pwcs, fc_encoding);
		if (fc_chwidth > 0)
			fc_width += fc_chwidth;

		fc_pwcs += fc_chlen;
		fc_len -= fc_chlen;
	}
	return fc_width;
}

/*
 * pg_wcssize获取给定编码下的给定字符串并返回三个
 * 值：
 *	  result_width：字符串中最长行的显示字符宽度
 *	  result_height：显示输出中的行数
 *	  result_format_size：存储字符串格式化
 *		表示所需的字节数
 *
 * 这必须与pg_wcsformat保持同步！
 */
void pg_wcssize(const unsigned char *fc_pwcs, size_t fc_len, int fc_encoding,
		   int *fc_result_width, int *fc_result_height, int *fc_result_format_size)
{
	int			fc_w,
				fc_chlen = 0,
				fc_linewidth = 0;
	int			fc_width = 0;
	int			fc_height = 1;
	int			fc_format_size = 0;

	for (; *fc_pwcs && fc_len > 0; fc_pwcs += fc_chlen)
	{
		fc_chlen = PQmblen((const char *) fc_pwcs, fc_encoding);
		if (fc_len < (size_t) fc_chlen)
			break;
		fc_w = PQdsplen((const char *) fc_pwcs, fc_encoding);

		if (fc_chlen == 1)			/* 单字节字符 */
		{
			if (*fc_pwcs == '\n')	/* 换行符 */
			{
				if (fc_linewidth > fc_width)
					fc_width = fc_linewidth;
				fc_linewidth = 0;
				fc_height += 1;
				fc_format_size += 1;	/* 对于NUL字符 */
			}
			else if (*fc_pwcs == '\r') /* 行馈 */
			{
				fc_linewidth += 2;
				fc_format_size += 2;
			}
			else if (*fc_pwcs == '\t') /* 制表符 */
			{
				do
				{
					fc_linewidth++;
					fc_format_size++;
				} while (fc_linewidth % 8 != 0);
			}
			else if (fc_w < 0)		/* 其他控制字符 */
			{
				fc_linewidth += 4;
				fc_format_size += 4;
			}
			else				/* 按原样输出 */
			{
				fc_linewidth += fc_w;
				fc_format_size += 1;
			}
		}
		else if (fc_w < 0)			/* 非ascii控制字符 */
		{
			fc_linewidth += 6;		/* \u0000 */
			fc_format_size += 6;
		}
		else					/* 所有其他字符 */
		{
			fc_linewidth += fc_w;
			fc_format_size += fc_chlen;
		}
		fc_len -= fc_chlen;
	}
	if (fc_linewidth > fc_width)
		fc_width = fc_linewidth;
	fc_format_size += 1;			/* 对于NUL字符 */

	/* 设置结果 */
	if (fc_result_width)
		*fc_result_width = fc_width;
	if (fc_result_height)
		*fc_result_height = fc_height;
	if (fc_result_format_size)
		*fc_result_format_size = fc_format_size;
}

/*
 * 将字符串格式化为一个或多个“struct lineptr”行。
 * lines[i].ptr == NULL表示数组的结束。
 *
 * 这必须与pg_wcssize保持同步！
 */
void pg_wcsformat(const unsigned char *fc_pwcs, size_t fc_len, int fc_encoding,
			 struct lineptr *fc_lines, int fc_count)
{
	int			fc_w,
				fc_chlen = 0;
	int			fc_linewidth = 0;
	unsigned char *fc_ptr = fc_lines->ptr;	/* 指向数据区域的指针 */

	for (; *fc_pwcs && fc_len > 0; fc_pwcs += fc_chlen)
	{
		fc_chlen = PQmblen((const char *) fc_pwcs, fc_encoding);
		if (fc_len < (size_t) fc_chlen)
			break;
		fc_w = PQdsplen((const char *) fc_pwcs, fc_encoding);

		if (fc_chlen == 1)			/* 单字节字符 */
		{
			if (*fc_pwcs == '\n')	/* 换行符 */
			{
				*fc_ptr++ = '\0';
				fc_lines->width = fc_linewidth;
				fc_linewidth = 0;
				fc_lines++;
				fc_count--;
				if (fc_count <= 0)
					exit(1);	/* 错误 */

				/* 使下一行指向剩余内存 */
				fc_lines->ptr = fc_ptr;
			}
			else if (*fc_pwcs == '\r') /* 行馈 */
			{
				strcpy((char *) fc_ptr, "\\r");
				fc_linewidth += 2;
				fc_ptr += 2;
			}
			else if (*fc_pwcs == '\t') /* 制表符 */
			{
				do
				{
					*fc_ptr++ = ' ';
					fc_linewidth++;
				} while (fc_linewidth % 8 != 0);
			}
			else if (fc_w < 0)		/* 其他控制字符 */
			{
				sprintf((char *) fc_ptr, "\\x%02X", *fc_pwcs);
				fc_linewidth += 4;
				fc_ptr += 4;
			}
			else				/* 按原样输出 */
			{
				fc_linewidth += fc_w;
				*fc_ptr++ = *fc_pwcs;
			}
		}
		else if (fc_w < 0)			/* 非ascii控制字符 */
		{
			if (fc_encoding == PG_UTF8)
				sprintf((char *) fc_ptr, "\\u%04X", fc_utf8_to_unicode(fc_pwcs));
			else
			{
				/*
				 * 在当前代码中此情况不能发生，因为仅
				 * UTF-8信号多字节控制字符。但是我们可能需要
				 * 在某个阶段支持它
				 */
				sprintf((char *) fc_ptr, "\\u????");
			}
			fc_ptr += 6;
			fc_linewidth += 6;
		}
		else					/* 所有其他字符 */
		{
			int			fc_i;

			for (fc_i = 0; fc_i < fc_chlen; fc_i++)
				*fc_ptr++ = fc_pwcs[fc_i];
			fc_linewidth += fc_w;
		}
		fc_len -= fc_chlen;
	}
	fc_lines->width = fc_linewidth;
	*fc_ptr++ = '\0';				/* 终止格式化字符串 */

	if (fc_count <= 0)
		exit(1);				/* 错误 */

	(fc_lines + 1)->ptr = NULL;	/* 终止行数组 */
}


/*
 * 编码验证：从字符串中删除任何无法验证的字符
 *
 * 这似乎与其他地方的现有功能重复？
 */
unsigned char * mbvalidate(unsigned char *fc_pwcs, int fc_encoding)
{
	if (fc_encoding == PG_UTF8)
		fc_mb_utf_validate(fc_pwcs);
	else
	{
		/*
		 * 其他需要验证的编码应在此添加自己的例程
		 */
	}

	return fc_pwcs;
}
